﻿using System;
using System.Linq;
using System.Linq.Expressions;
using ProLinq.Toolkit;

namespace ProLinq.Projection.Configuration
{
	internal class ProjectionSource<TSource> : IProjectionSource<TSource>
	{
		private readonly IQueryable<TSource> _source;

		public ProjectionSource(IQueryable<TSource> source)
		{
			this._source = source;
		}

		#region IProjectionSource Members

		public IQueryable<TResult> To<TResult>(ProjectionConfiguration<TSource, TResult> configuration)
		{
			var provider = new ProjectQueryProvider(this._source, configuration);
			return new Query<TResult>(provider);
		}

		public IQueryable<TResult> To<TResult>()
			where TResult : new()
		{
			ProjectionConfiguration<TSource, TResult> configuration =
				ProjectionConfigurationFactory.Instance.CreateConfiguration<TSource, TResult>();
			return this.To(configuration);
		}

		public IQueryable<TResult> To<TResult>(Expression<Func<TSource, TResult>> factory)
		{
			ProjectionConfiguration<TSource, TResult> configuration =
				ProjectionConfigurationFactory.Instance.CreateConfiguration(factory);
			var provider = new ProjectQueryProvider(this._source, configuration);
			return new Query<TResult>(provider);
		}

		public IQueryable<TResult> To<TResult>(Action<ProjectionConfiguration<TSource, TResult>> configure)
		{
			ProjectionConfiguration<TSource, TResult> configuration =
				ProjectionConfigurationFactory.Instance.CreateConfiguration<TSource, TResult>();
			configure(configuration);
			return this.To(configuration);
		}

		public IQueryable<TResult> To<TResult>(Expression<Func<TSource, TResult>> factory, Action<ProjectionConfiguration<TSource, TResult>> configure)
		{
			ProjectionConfiguration<TSource, TResult> configuration =
				ProjectionConfigurationFactory.Instance.CreateConfiguration<TSource, TResult>(factory);
			configure(configuration);
			return this.To(configuration);
		}

		#endregion
	}
}